// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.

// The code is based on on AVS-input
// from x264 (http://www.videolan.org/developers/x264.html).

// You can use this software to encode videos using the 32-bit version
// of Avisynth with the 64-bit version of x264 under Windows.
// The x264 executable needs to be named x264_64.exe and placed in the
// same folder as this program. Otherwise use --x264-binary "x264_path"
// to define the pach of x264 binary.
// Example:
// avs4x264.exe --x264-binary "C:\x264_64-abc.exe" -o out.264 in.avs

// Modified by 06_taro ( astrataro@gmail.com ).
// Modifications: 
// -- When x264's parameter "input-depth" set and is not equal to 8, 
//	divide "width" by 2. This make faked 16-bit avs output, i.e.,
//	MSB and LSB field interleaved clip, be treated correctly by x264.
//	If "input-depth" not defined or equals to 8, avs4x264mod acts in
//	the same way as original avs4x264.
// -- Print full command-line piped in to x264_64.exe to screen,
//	prefixed by "avs4x264 [info]:".
// -- Make x264_64.exe path changeable. The path of x264 binary can be
//	set by --x264-binary "x264_path"
//	Example:
//	avs4x264.exe --x264-binary "C:\x264_64-abc.exe" -o out.264 in.avs

//Compiling: gcc avs4x264mod.c -s -O2 -oavs4x264mod

#include "avs4x264mod.h"

extern avs_hnd_t avs_h;
extern buffer_t OBuffer;

int _writefile(HANDLE hFile, char * lpBuffer, int nNumberOfBytesToWrite)
{
	DWORD temp;
	return WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &temp, NULL);
}

int _mt_writefile(HANDLE hFile, char * lpBuffer, int nNumberOfBytesToWrite)
{
	if(OBuffer.size > OBuffer.capacity >> 1)
		usleep((OBuffer.size << 10) - 250000);
	while(OBuffer.size >= OBuffer.capacity)
	{
		// Buffer full
		//printf("\t Buffer: %d/%d --+->\r", OBuffer.size, OBuffer.capacity);
		usleep(500000);
	}
	//char * buf = (char *) malloc(nNumberOfBytesToWrite);
	node_t* lnk = OBuffer.dynarray+OBuffer.tail;
	//memcpy(buf, lpBuffer, nNumberOfBytesToWrite);
	//printf("+%d~%d ", nNumberOfBytesToWrite, OBuffer.tail);
	//lnk->data = buf;
	lnk->data = lpBuffer;
	lnk->length = nNumberOfBytesToWrite;
	OBuffer.tail++;
	if(OBuffer.tail >= OBuffer.capacity)
		OBuffer.tail -= OBuffer.capacity;
	OBuffer.size++;
	return 1;
}

int main(int argc, char *argv[])
{
	DWORD exitcode = 0;
	//avs related
	int result = 0;
	int len;
	/*Video Info*/
	video_info_t VideoInfo = {0};
	pipe_info_t PipeInfo = {0};
	/*
	int i_width;
	int i_height;
	int i_fps_num;
	int i_fps_den;
	int i_frame_total;
	int b_interlaced=0;
	char *infile = NULL;
	*/
	/*Video Info End*/
	int i;
	if (argc <= 1)
	{
		puts("     avs4x264modex, the ultimate Avisynth input piper for x264");
		puts("             Based on the avs-input from x264 code\n");
		printf("Usage: %s [avs4x264 options] [x264 options] <input>.avs\n", argv[0]);
		puts("Options:");
		puts(" --x264-binary <file>          User defined x264 binary path");
		puts(" --input-depth <arg>           Color depth of the input");
		puts("                               Width gets half if arg=16");
		puts(" --affinity <cpu>              Set Process Affinity for the piper");
		puts(" --x264-affinity <cpu>         Set Process Affinity for x264");
		puts(" --pipe-mt                     Seperated thread for pipe buffer\n");
		puts("This program is free software; you can redistribute it and/or modify");
		puts("it under the terms of the GNU General Public License as published by");
		puts("the Free Software Foundation; either version 2 of the License, or");
		puts("(at your option) any later version.");
		return -1;
	}
	//get the script file and other informations from the commandline
	for (i=1;i<argc;i++)
	{
		len =  strlen(argv[i]);
		if (len>4 && (argv[i][len-4])== '.' && tolower(argv[i][len-3])== 'a' && tolower(argv[i][len-2])== 'v' && tolower(argv[i][len-1])== 's')
		{
			VideoInfo.infile=argv[i];
			break;
		}
		else if((len==12)&&(strcmp(argv[i], "--interlaced")==0))
		{
			fprintf( stderr, "--interlaced found.\n");
			VideoInfo.b_interlaced=1;
		}
	}
	if (!VideoInfo.infile)
	{
		fprintf( stderr, "No avs script found.\n");
		return -1;
	}

	result = LoadAVSFile(&VideoInfo);
	if(result == ERR_AVS_NOTFOUND)
		return -1;
	if(result == ERR_AVS_FAIL)
		goto avs_fail;
	
	result = CreateX264Process(argc, argv, &VideoInfo, &PipeInfo);
	if(result == ERR_AVS_FAIL)
		goto avs_fail;
	if(result == ERR_PIPE_FAIL)
		goto pipe_fail;

	if(VideoInfo.b_mtpipe)
	{
		fprintf( stderr, "avs4x264 [info]: Multi-threaded pipe buffer enabled.\n");
	}
	result = WritePipeLoop(VideoInfo.b_mtpipe ? &_mt_writefile : &_writefile, &VideoInfo, &PipeInfo);
	if(result == ERR_PROCESS_FAIL)
		goto process_fail;
	OBuffer.size++;
	
	if(VideoInfo.b_mtpipe)
	{
		// more cleanups
		if(OBuffer.size > 0)
			fprintf( stderr, "avs4x264 [info]: Waiting background worker to finish his job...\n" );
		
		while(OBuffer.size > 0)
		{
			fprintf( stderr, "\t\t\t\t\t\t\t\t\t\t Buffer: %d <-- \r", OBuffer.size);
			fflush(stderr);
			sleep(1);
		}
		OBuffer.size = -1; // and sub-thread will exit automatically.
	}

	//close & cleanup
process_fail:// everything created
	CloseHandle(PipeInfo.h_pipeWrite);// h_pipeRead already closed
	WaitForSingleObject(PipeInfo.pi_info.hProcess, INFINITE);
	GetExitCodeProcess(PipeInfo.pi_info.hProcess,&exitcode);
	CloseHandle(PipeInfo.pi_info.hProcess);
	goto avs_cleanup;// pipes already closed
pipe_fail://pipe created but failed after that
	CloseHandle(PipeInfo.h_pipeRead);
	CloseHandle(PipeInfo.h_pipeWrite);
avs_fail://avs enviormnet created but failed after that
	exitcode = -1;
avs_cleanup:
	avs_h.func.avs_release_clip( avs_h.clip );
	if( avs_h.func.avs_delete_script_environment )
		avs_h.func.avs_delete_script_environment( avs_h.env );
	FreeLibrary( avs_h.library );
	return exitcode;
}
